//-------------------------------------------------------------------
//	Copyright (c) 2012-2014 Zhirnov Andrey
//	This file is part of the "UXGEN-ES" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#include "Platforms/AndroidEmulator/WAndroidEmulator.h"

#if defined( PLATFORM_WINDOWS ) and defined( ANDROID_EMULATOR )

#include "Platforms/Windows/WPlatform.h"

#include "Platforms/AndroidEmulator/ContextEGLEmulator.h"
#include "Platforms/Android/APlatform.h"
#include "Platforms/AndroidEmulator/HwCameraEmulator.h"

namespace Engine
{
namespace Platforms
{


	//
	// Android Java Interface
	//

	class AndroidJavaInterface
	{
	// types
	public:
		struct JavaEnvSetter
		{
			JavaEnvSetter ()
			{
				AndroidPlatform::GetInstance()->GetJavaInterface()->_javaEnvAvailable = true;
			}

			~JavaEnvSetter ()
			{
				AndroidPlatform::GetInstance()->GetJavaInterface()->_javaEnvAvailable = false;
			}
		};
		
		typedef void (* MessageCallback_t) (const BaseObjectPtr &obj, int result);


	// variables
	private:
		AndroidPlatform &		_platform;
		bool					_javaEnvAvailable;
		HardwareCameraEmulator	_camera;


	// methods
	public:
		AndroidJavaInterface (AndroidPlatform &platform) :
			_platform( platform ), _javaEnvAvailable(false)
		{}

		~AndroidJavaInterface ()
		{}

		bool IsJavaEnvAvailable ()	const	{ return _javaEnvAvailable; }


		// Java Functions Emulation //
		void CloseActivity ();
		
		void Notification (const string &text, bool longTime);
		
		void OpenURL (const string &url);

		void Message (int id, const string &caption, const string &text,
					  const string &positive, const string &negative);
		
		void SendEmail (const string &address, const string &subject,
						const string &text, const string &attachment);
		
		void BackgroundMode (bool enabled);

		void EnableAudio (bool enabled);
		
		void Vibrate (const uni_array<const int64> &pattern);
		
		void ProcessEngineEvent (uint type, uint param0, uint param1, uint param2);
		void SetDisplayOrientation (EDisplayOrientation::type orient);
		
		void CheckLicense (const IPlatform::OnLicenseChecked_t &callback, const string &publicKey, const string &deviceId);
		
		// Extensions
		void EnableExtension (int ext, bool enable);
		void CameraCommand (int cmd, float arg0, float arg1, float arg2)	{ _camera.Command( cmd, arg0, arg1, arg2 ); }

		// only for emulator
		void OnUpdate (bool redraw);
	};


#	define JAVA_ENV()			AndroidJavaInterface::JavaEnvSetter	__javaEnvSetter()
#	define APLATFORM()			AndroidPlatform::GetInstance()
#	define AJAVAINTERFACE()		APLATFORM()->GetJavaInterface()
#	define USE_JAVA_ENV()		CHECK( AndroidPlatform::GetInstance()->GetJavaInterface()->IsJavaEnvAvailable() )




	//
	// Android Emulator
	//

	class AndroidEmulator : public BaseObject, public WindowsPlatform
	{
	// types
	private:
		SHARED_POINTER( AndroidEmulator );

		struct Instance
		{
			~Instance ()
			{
				AndroidEmulator::_instance = null;
			}
		};


	// variables
	private:
		Instance			_inst;
		AndroidPlatform		_androidPlatform;

		AndroidHwCamera		_camera;

		bool				_contextInitialized;
		bool				_surfaceCreated;
		bool				_windowCreated;

		static AndroidEmulator *	_instance;


	// methods
	public:
		AndroidEmulator ();
		~AndroidEmulator ();

		virtual int Loop () override;
		virtual void _Update (bool redraw) override;
		virtual void _OnResized (uint w, uint h) override;
		virtual void _OnMove (const ivec2 &delta) override;
		virtual void SendEvent (const SysEvent &ev) override;
		virtual void SendEventDelayed (const SysEvent &ev) override;
		
		void CreateWindow ();
		void DestroyWindow ();
		
		void Resize ();

		virtual Ptr< HardwareCamera >	GetCamera () override	{ return &_camera; }

		static Ptr< AndroidEmulator >	GetEmulator ()			{ return _instance; }


		// ContextEGL //
		bool InitRender (INOUT VideoSettings &vs) override;
		void DestroyRender ();
		bool CreateSurface (void *window);
		void DestroySurface ();
		bool IsSurfaceCreated () const;
		bool MakeCurrent ();
		bool ResetContext ();
		bool IsContextCurrent ();
		bool IsContextInitialized ();


		// Settings //
		static float	GetAccelerationScale ()		{ return 2.0f; }
	};




//------------------------- AndroidEmulator -----------------------//

#	define EMULATOR()	AndroidEmulator::GetEmulator()

	AndroidEmulator *	AndroidEmulator::_instance = null;

	
/*
=================================================
	constructor
=================================================
*/
	AndroidEmulator::AndroidEmulator () :
		BaseObject( _androidPlatform.GetSubSystems() ),
		_camera( ESS() ),
		_contextInitialized(false), _surfaceCreated(false),
		_windowCreated(false)
	{
		_instance = this;
	}
	
/*
=================================================
	destructor
=================================================
*/
	AndroidEmulator::~AndroidEmulator ()
	{
		ASSERT( _instance == this );
		//_instance = null;		// moved to Instance class
	}
	
/*
=================================================
	Loop
=================================================
*/
	int AndroidEmulator::Loop ()
	{
		AndroidPlatform &	ap = _androidPlatform;

		ap.Init(0);
		InitDevice( _androidPlatform.GetDeviceType() );

		for (uint i = 0; i < EDirectory::_COUNT; ++i)
		{
			const EDirectory::type  type = EDirectory::type( i );
			ap.SetDirectory( GetDirectoryPath( type ), type );
		}

		ap.SetDevice( new Device( GetDevice() ) );

		ap.EnterForeground( false );
		CreateWindow();
		ap.EnterForeground( true );

		ap.SurfaceChanged( AndroidWindowSurface(), GetDisplay().Resolution().x, GetDisplay().Resolution().y );

		_EventLoop();

		ap.EnterBackground( false );
		ap.EnterBackground( true );

		ap.SurfaceDestroyed();
		
		DestroyWindow();
		return 0;
	}
	
/*
=================================================
	_Update
=================================================
*/
	void AndroidEmulator::_Update (bool redraw)
	{
		AndroidPlatform &	ap = _androidPlatform;
		
		AJAVAINTERFACE()->OnUpdate( redraw );

		Thread::Delay( ap.ProcessMessages() );
		ap.Flush();
	}
	
/*
=================================================
	_OnResized
=================================================
*/
	void AndroidEmulator::_OnResized (uint w, uint h)
	{
		_windowSize = ivec2( w, h );
		_androidPlatform.Resize( w, h );
	}
	
/*
=================================================
	_OnMove
=================================================
*/
	void AndroidEmulator::_OnMove (const ivec2 &delta)
	{
		const fvec2	n = delta.To<fvec2>() / (float)_GetScreenResolution().Max() * GetAccelerationScale();

		_androidPlatform.OnAcceleration( n.x, n.y, 10.0f );
	}

/*
=================================================
	SendEvent
=================================================
*/
	void AndroidEmulator::SendEvent (const SysEvent &ev)
	{
		_androidPlatform.SendEvent( ev );
	}
	
/*
=================================================
	SendEventDelayed
=================================================
*/
	void AndroidEmulator::SendEventDelayed (const SysEvent &ev)
	{
		_androidPlatform.SendEvent( ev );
	}

/*
=================================================
	CreateWindow
=================================================
*/
	void AndroidEmulator::CreateWindow ()
	{
		if ( _windowCreated )
			return;

		CHECK_ERR( WindowsPlatform::_RegisterClass(), void() );
		CHECK_ERR( WindowsPlatform::_CreateWindow(), void() );
		CHECK_ERR( WindowsPlatform::_InitRawInput(), void() );
		_windowCreated = true;
	}
	
/*
=================================================
	DestroyWindow
=================================================
*/
	void AndroidEmulator::DestroyWindow ()
	{
		if ( _windowCreated )
		{
			WindowsPlatform::_Destroy();
			_windowCreated = false;
		}
	}

/*
=================================================
	InitRender
=================================================
*/
	bool AndroidEmulator::InitRender (INOUT VideoSettings &vs)
	{
		CreateWindow();

		_contextInitialized	= WindowsPlatform::_InitOpenGL( vs );
		_surfaceCreated		= false;
		return _contextInitialized;
	}
	
/*
=================================================
	DestroyRender
=================================================
*/
	void AndroidEmulator::DestroyRender ()
	{
		_contextInitialized	= false;
		_surfaceCreated		= false;

		if ( _dc != null )
		{
			wglMakeCurrent( _dc, null );

			if ( _rc != null )
			{
				wglDeleteContext( _rc );
				_rc = null;
			}

			ReleaseDC( _wnd, _dc );
			_dc = null;
		}
	}
	
/*
=================================================
	CreateSurface
=================================================
*/
	bool AndroidEmulator::CreateSurface (void *window)
	{
		CHECK_ERR( _contextInitialized );
		_surfaceCreated = true;
		MakeCurrent();
		return true;
	}
	
/*
=================================================
	DestroySurface
=================================================
*/
	void AndroidEmulator::DestroySurface ()
	{
		CHECK_ERR( not _surfaceCreated or _contextInitialized, void() );
		ResetContext();
		_surfaceCreated = false;
	}
	
/*
=================================================
	IsSurfaceCreated
=================================================
*/
	bool AndroidEmulator::IsSurfaceCreated () const
	{
		return _surfaceCreated;
	}
	
/*
=================================================
	MakeCurrent
=================================================
*/
	bool AndroidEmulator::MakeCurrent ()
	{
		CHECK_ERR( _dc != null and _rc != null );
		CHECK_ERR( _surfaceCreated );

		bool	ret = wglMakeCurrent( _dc, _rc ) == TRUE;
		
		if ( _ext.swapControlSupported )
		{
			_ext.wglSwapInterval( 1 );
		}

		return ret;
	}
	
/*
=================================================
	ResetContext
=================================================
*/
	bool AndroidEmulator::ResetContext ()
	{
		CHECK_ERR( _dc != null );
		
		return wglMakeCurrent( _dc, null ) == TRUE;
	}
	
/*
=================================================
	IsContextCurrent
=================================================
*/
	bool AndroidEmulator::IsContextCurrent ()
	{
		CHECK_ERR( _rc != null );
		return wglGetCurrentContext() == _rc;
	}
	
/*
=================================================
	IsContextInitialized
=================================================
*/
	bool AndroidEmulator::IsContextInitialized ()
	{
		return _contextInitialized;
	}
	
/*
=================================================
	Resize
=================================================
*/
	void AndroidEmulator::Resize ()
	{
		_device.GetDisplayRef().SetResolution( _windowSize );

		_Resize( GetDisplay().Resolution().yx() );
	}



	
//----------------------- AndroidJavaInterface --------------------//
/*
=================================================
	CloseActivity
=================================================
*/
	void AndroidJavaInterface::CloseActivity ()
	{
		EMULATOR()->Terminate();
	}
	
/*
=================================================
	Notification
=================================================
*/
	void AndroidJavaInterface::Notification (const string &text, bool longTime)
	{
		WARNING( (string("Notification:\n") << text).cstr() );
	}
	
/*
=================================================
	OpenURL
=================================================
*/
	void AndroidJavaInterface::OpenURL (const string &url)
	{
		EMULATOR()->OpenURL( url );
	}

/*
=================================================
	Message
=================================================
*/
	void AndroidJavaInterface::Message (int id, const string &caption, const string &text,
										const string &positive, const string &negative)
	{
		using namespace winapi;

		int	buttons = 0;

		if ( positive != null )
		{
			if ( negative == null )	buttons = MB_OK;
			else					buttons = MB_OKCANCEL;
		}
		else
		{
			if ( negative == null )	buttons = 0;
			else					{ buttons = MB_OK;  WARNING( "only negative button not supported!" ); }
		}

		int res = MessageBox( null, text.cstr(), caption.cstr(), buttons | MB_SETFOREGROUND | MB_TOPMOST );

		switch ( res )
		{
			case IDOK :
				APLATFORM()->MessageResult( id, 1 );
				break;

			default :
				APLATFORM()->MessageResult( id, -1 );
				break;
		}
	}
	
/*
=================================================
	SendEmail
=================================================
*/
	void AndroidJavaInterface::SendEmail (const string &address, const string &subject,
										  const string &text, const string &attachment)
	{
		TODO( "SendEmail" );
	}

/*
=================================================
	BackgroundMode
=================================================
*
	void AndroidJavaInterface::BackgroundMode (bool enabled)
	{
		EMULATOR()->BackgroundMode( enabled );
	}
	
/*
=================================================
	EnableAudio
=================================================
*/
	void AndroidJavaInterface::EnableAudio (bool enabled)
	{
		EMULATOR()->EnableAudio( enabled );
	}
	
/*
=================================================
	Vibrate
=================================================
*/
	void AndroidJavaInterface::Vibrate (const uni_array<const int64> &pattern)
	{
		TODO( "Vibrate" );
	}
	
/*
=================================================
	ProcessEngineEvent
=================================================
*/
	void AndroidJavaInterface::ProcessEngineEvent (uint type, uint param0, uint param1, uint param2)
	{
		TODO( "ProcessEngineEvent" );
	}
	
/*
=================================================
	SetDisplayOrientation
=================================================
*/
	void AndroidJavaInterface::SetDisplayOrientation (EDisplayOrientation::type orient)
	{
		Display::EOrientation	orientation = Display::ORIENT_0;
		
		switch ( orient )
		{
			case EDisplayOrientation::DEFAULT :
			case EDisplayOrientation::SENSOR :
			case EDisplayOrientation::PORTRAIT :
				orientation = Display::ORIENT_0;
				break;

			case EDisplayOrientation::PORTRAIT_REVERSE :
				orientation = Display::ORIENT_180;
				break;

			case EDisplayOrientation::LANDSCAPE :
				orientation = Display::ORIENT_90;
				break;

			case EDisplayOrientation::LANDSCAPE_REVERSE :
				orientation = Display::ORIENT_270;
				break;
		}

		APLATFORM()->UpdateDisplayOrientation( orientation );

		EMULATOR()->Resize();
	}
	
/*
=================================================
	CheckLicense
=================================================
*/
	void AndroidJavaInterface::CheckLicense (const IPlatform::OnLicenseChecked_t &callback, const string &publicKey, const string &deviceId)
	{
		ParallelOp	op;
		FunctionBuilder::Create( op.func, &AndroidPlatform::OnLicenseChecked, callback, ELicenseCheckResponse::LICENSED, "" );

		APLATFORM()->Push( op );
	}

/*
=================================================
	EnableExtension
=================================================
*/
	void AndroidJavaInterface::EnableExtension (int ext, bool enable)
	{
		switch ( ext )
		{
			case AndroidPlatform::Extension::CAMERA :
				_camera.Enable( enable );
				break;
		}
	}
	
/*
=================================================
	OnUpdate
=================================================
*/
	void AndroidJavaInterface::OnUpdate (bool redraw)
	{
		_camera.Update();
	}

	

//---------------------------- ContextEGL -------------------------//

	bool ContextEGL::Initialize (Descriptor &desc)
	{
		return EMULATOR()->InitRender( ReferenceCast< VideoSettings >( desc ) );
	}

	void ContextEGL::Destroy ()
	{
		return EMULATOR()->DestroyRender();
	}

	bool ContextEGL::CreateSurface (void *window)
	{
		return EMULATOR()->CreateSurface( window );
	}

	void ContextEGL::DestroySurface ()
	{
		return EMULATOR()->DestroySurface();
	}

	bool ContextEGL::IsSurfaceCreated () const
	{
		return EMULATOR()->IsSurfaceCreated();
	}

	bool ContextEGL::SwapBuffers ()
	{
		EMULATOR()->SwapBuffers();
		return true;
	}

	bool ContextEGL::SwapInterval (int interval)
	{
		return false;
	}

	bool ContextEGL::MakeCurrent ()
	{
		return EMULATOR()->MakeCurrent();
	}

	bool ContextEGL::ResetContext ()
	{
		return EMULATOR()->ResetContext();
	}

	bool ContextEGL::IsContextCurrent () const
	{
		return EMULATOR()->IsContextCurrent();
	}

	bool ContextEGL::IsContextInitialized () const
	{
		return EMULATOR()->IsContextInitialized();
	}

#undef EMULATOR

}	// Platforms
}	// Engine



/*
=================================================
	main
=================================================
*/
int main (int argc, char *argv[])
{
	using namespace Engine;
	using namespace Platforms;

	AndroidEmulator	emulator;
	int ret = emulator.Loop();
	
	DEBUG_ONLY( Referenced::s_ChenckNotReleasedObjects() );

	return ret;
}


#endif	// PLATFORM_WINDOWS and ANDROID_EMULATOR